package com.john.utils.common;

/**
 * FPS帧率控制
 */
public class FpsUtils {
    private final long NANO_IN_MILLI = 1000000L;
    private final int NO_DELAYS_PER_YIELD = 16;  //不延迟每单位产量
    private final int MAX_RENDER_SKIPS = 5;    //最大渲染跳过数量
    private int TARGET_FPS = 30; //目标FPS帧率，帧率越高画面越流畅，但会消耗更多性能
    long fps;
    long frameCounter;
    long lastFpsTime;
    private long gameStartTime;
    private long curRenderTime;
    private long rendersSkipped = 0L;
    private long period; // 渲染之间的时间间隔
    private Callback callback;  //执行回调
    private boolean isRun = true;   //是否执行

    public FpsUtils(Callback callback,int TARGET_FPS) {
        this.TARGET_FPS = TARGET_FPS;
        this.callback = callback;
    }

    /**
    * Fps帧率控制执行
    */
    public void start() {
        try {
            // loop initialization
            long beforeTime, afterTime, timeDiff, sleepTime;
            long overSleepTime = 0L;
            int noDelays = 0;
            long excess = 0L;
            gameStartTime = System.nanoTime();
            //prevStatsTime = gameStartTime;
            beforeTime = gameStartTime;

            period = (1000L * NANO_IN_MILLI) / TARGET_FPS; // rendering FPS (nanosecs/targetFPS)
            System.out.println("FPS: " + TARGET_FPS);
            System.out.println("FPS period: " + period);
            callback.getFPSPeriod(period);

            // Main loop
            while (isRun) {
                // **2) execute physics
                callback.executionMain();   //执行FPS帧率主体程序

                afterTime = System.nanoTime();
                curRenderTime = afterTime;
                calculateFramesPerSecond();

                timeDiff = afterTime - beforeTime;
                sleepTime = (period - timeDiff) - overSleepTime;
                if (sleepTime > 0) { // time left in cycle
                    Thread.sleep(sleepTime / NANO_IN_MILLI);//nano->ms
                    overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
                } else { // sleepTime <= 0;
                    System.out.println("Rendering too slow");
                    // this cycle took longer than period
                    excess -= sleepTime;
                    // store excess time value
                    overSleepTime = 0L;
                    if (++noDelays >= NO_DELAYS_PER_YIELD) {
                        Thread.yield();
                        // give another thread a chance to run
                        noDelays = 0;
                    }
                }
                beforeTime = System.nanoTime();
                int skips = 0;
                while ((excess > period) && (skips < MAX_RENDER_SKIPS)) {
                    // update state but don’t render
                    System.out.println("跳过renderFPS，运行updateFPS");
                    excess -= period;
                    // **2) execute physics
                    callback.executionMain();   //执行FPS帧率主体程序
                    skips++;
                }
                rendersSkipped += skips;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * fps计算
     */
    private void calculateFramesPerSecond() {
        if(curRenderTime - lastFpsTime >= NANO_IN_MILLI*1000) {
            fps = frameCounter;
            frameCounter = 0;
            lastFpsTime = curRenderTime;
        }
        frameCounter++;
        callback.getFps(fps);
    }

    //fps帧率回调接口，必须实现
    public interface Callback {
        /**
         * Fps帧率执行主体
         */
        void executionMain();

        /**
         * 当前Fps帧率
         * @return
         */
        void getFps(long fps);

        /**
         * 渲染之间的时间间隔
         * @return
         */
        void getFPSPeriod(long fpsPeriod);

    }

    //停止执行fsp帧率计算
    public void stop(){
        this.isRun = false;
    }

    //当前是否在执行
    public boolean isRun(){
        return this.isRun;
    }

}